// i.r(t), i.d(t, { default: () => CanvasModule })
import Module from "./Module"
import CanvasData from "../data/CanvasData"
import { PlayerMovedMessage, PlayerResizedMessage } from "../message/play.message"
import OpenDeferred from "../utils/OpenDeferred"
import { ScreenToCanvasPointCommand } from "../command/screen.command"
import funcUtil from "../utils/func.util"
import { BlurCanvasCommand, ResizeCanvasCommand } from "../command/canvas.command"
import u from "../packages/85917"
import { CanvasMountError } from "../exception/canvas.exception"
import keyConst from "../constant/key.const"

const m = {
  [keyConst.width]: "width",
  [keyConst.height]: "height",
  [keyConst.top]: "top",
  [keyConst.left]: "left"
}
export default class CanvasModule extends Module {
  getNewX: (v?: any) => number
  getNewY: (v?: any) => number
  getNewWidth: (e: any) => any
  getNewHeight: (e: any) => any
  activeTransitions: any[]
  activeTransitionMap: { left: any; top: any; width: any; height: any }
  onResize: (e: any) => void
  _canvas: HTMLElement
  onWindowResize: () => void
  transitionDurations: any
  resizeCanvas: (e: any) => Promise<any>
  canvasBounds: any
  engine: any
  container: any
  containerBounds: any
  data: CanvasData
  constructor() {
    super(...arguments)
    this.name = "canvas"
    this.getNewX = () => 0
    this.getNewY = () => 0
    this.getNewWidth = e => e.width
    this.getNewHeight = e => e.height
    this.activeTransitions = []
    this.activeTransitionMap = {
      left: null,
      top: null,
      width: null,
      height: null
    }
    this.onResize = e => {
      e.propertyName in keyConst &&
        (this.resolveActiveTransition(e.propertyName), (this._canvas.style.transitionDuration = this.stringifyTransitionDurations()))
    }
    this.onWindowResize = () => {
      this.transitionDurations.left = this.transitionDurations.top = this.transitionDurations.width = this.transitionDurations.height = "0s"
      this._canvas.style.transitionDuration = this.stringifyTransitionDurations()
      this.applySize()
      this.applyPosition()
      this.activeTransitions.forEach(this.resolveActiveTransition.bind(this))
      this._canvas.style.transitionDuration = this.stringifyTransitionDurations()
    }
    this.resizeCanvas = async e => {
      if (e.resizeDimensions) {
        this.activeTransitions.forEach(this.resolveActiveTransition.bind(this))
        e.resizeDimensions.forEach(e => {
          const { property: t, setDimension: i, duration: n = 0 } = e,
            s = m[t],
            r = this._canvas.style[s]
          switch (t) {
            case keyConst.height:
              this.setHeight(i, n)
              break
            case keyConst.width:
              this.setWidth(i, n)
              break
            case keyConst.top:
              this.setY(i, n)
              break
            case keyConst.left:
              this.setX(i, n)
          }
          if (this._canvas.style[s] !== r && n > 0) {
            const e = new OpenDeferred()
            e.then(() => {
              clearTimeout(i)
            })
            const i = setTimeout(() => {
              this.resolveActiveTransition(t)
            }, n + 500)
            ;(this.activeTransitionMap[t] = e), this.activeTransitions.push(t)
          }
        })
        this.hasActiveTransitions() || (this.canvasBounds = this._canvas.getBoundingClientRect())
        return this.getTransitionPromise()
      }
    }
  }
  async init(e, t) {
    this.engine = t
    if (!e.container) throw new CanvasMountError()
    this.container = e.container
    this.container.appendChild(e.canvas)
    this.containerBounds = this.container.getBoundingClientRect()
    this._canvas = e.canvas
    this._canvas.setAttribute("tabindex", "-1")
    this._canvas.style.position = "absolute"
    this._canvas.style.left = "0"
    this._canvas.style.top = "0"
    this.transitionDurations = {
      left: "0s",
      top: "0s",
      width: "0s",
      height: "0s",
      filter: "0.4s"
    }
    this._canvas.style.transitionProperty = Object.keys(this.transitionDurations).join(",")
    this._canvas.style.transitionDuration = this.stringifyTransitionDurations()
    this.updateTransitions()
    this.bindings.push(
      u.createSubscription(u.resizeEnum.RESIZE, this.onWindowResize),
      t.commandBinder.addBinding(ResizeCanvasCommand, this.resizeCanvas),
      this.makeTransitionEndSubscription(),
      t.commandBinder.addBinding(ScreenToCanvasPointCommand, async e => this.screenToCanvasPoint(e.x, e.y)),
      t.commandBinder.addBinding(BlurCanvasCommand, this.blur.bind(this))
    )
    for (const e of this.bindings) e.renew()
    this.canvasBounds = this._canvas.getBoundingClientRect()
    this.containerBounds = this.container.getBoundingClientRect()
    this.data = new CanvasData(this.canvasBounds.width, this.canvasBounds.height)
    t.market.register(this, CanvasData, this.data)
  }
  makeTransitionEndSubscription() {
    let e = !1
    return {
      renew: () => {
        e || (this.element.addEventListener("transitionend", this.onResize), (e = !0))
      },
      cancel: () => {
        e && (this.element.removeEventListener("transitionend", this.onResize), (e = !1))
      }
    }
  }
  dispose(e) {
    super.dispose(e)
  }
  get element() {
    return this._canvas
  }
  get width() {
    return this.canvasBounds.width
  }
  get height() {
    return this.canvasBounds.height
  }
  get x() {
    return this.canvasBounds.left
  }
  get y() {
    return this.canvasBounds.top
  }
  onUpdate() {
    const e = this.container.getBoundingClientRect()
    const t = this.containerHasChanged(e)
    if (t) {
      this.containerBounds = e
      this.updateTransitions()
    }
    if (t || this.hasActiveTransitions()) {
      this.canvasBounds = this._canvas.getBoundingClientRect()
    }
    const i = this.data.width !== this.width || this.data.height !== this.height
    const n = this.data.x !== this.x || this.data.y !== this.y
    if (i) {
      this.data.width = this.width
      this.data.height = this.height
      this.data.commit()
      this.engine.broadcast(new PlayerResizedMessage(this.width, this.height))
    }
    if (n) {
      this.data.x = this.x
      this.data.y = this.y
      this.data.commit()
      this.engine.broadcast(new PlayerMovedMessage(this.x - this.containerBounds.left, this.y - this.containerBounds.top))
    }
  }
  containerHasChanged(e) {
    return (
      e.left !== this.containerBounds.left ||
      e.top !== this.containerBounds.top ||
      e.width !== this.containerBounds.width ||
      e.height !== this.containerBounds.height
    )
  }
  setX(e, t = 0) {
    this.getNewX = "number" == typeof e ? () => e : e
    this.transitionDurations.left = `${t}ms`
    this.updateTransitions()
  }
  setY(e, t = 0) {
    this.getNewY = "number" == typeof e ? () => e : e
    this.transitionDurations.top = `${t}ms`
    this.updateTransitions()
  }
  setWidth(e, t = 0) {
    this.getNewWidth = "number" == typeof e ? () => e : e
    this.transitionDurations.width = `${t}ms`
    this.updateTransitions()
  }
  setHeight(e, t = 0) {
    this.getNewHeight = "number" == typeof e ? () => e : e
    this.transitionDurations.height = `${t}ms`
    this.updateTransitions()
  }
  screenToCanvasPoint(e, t) {
    return { x: e - this.x, y: t - this.y }
  }
  async blur(e) {
    this.transitionDurations.filter = `${e.duration}ms`
    this._canvas.style.transitionDuration = this.stringifyTransitionDurations()
    this._canvas.style.filter = `blur(${e.pixelBlur}px)`
    await funcUtil.waitRun(e.duration)
    0 === e.pixelBlur && (this._canvas.style.filter = "")
  }
  getTransitionPromise() {
    return Promise.all(this.activeTransitions.map(e => this.activeTransitionMap[e]).filter(e => !!e))
  }
  updateTransitions() {
    this._canvas.style.transitionDuration = this.stringifyTransitionDurations()
    this.applySize()
    this.applyPosition()
  }
  applySize() {
    this._canvas.style.width = `${this.getNewWidth(this.containerBounds)}px`
    this._canvas.style.height = `${this.getNewHeight(this.containerBounds)}px`
  }
  applyPosition() {
    this._canvas.style.left = `${this.getNewX(this.containerBounds)}px`
    this._canvas.style.top = `${this.getNewY(this.containerBounds)}px`
  }
  stringifyTransitionDurations() {
    return Object.keys(this.transitionDurations)
      .map(e => this.transitionDurations[e])
      .join(",")
  }
  resolveActiveTransition(e) {
    const t = this.activeTransitions.indexOf(e)
    ;-1 !== t && this.activeTransitions.splice(t, 1)
    const i = this.activeTransitionMap[e]
    i && i.resolve(), (this.activeTransitionMap[e] = null), (this.transitionDurations[e] = "0s")
  }
  hasActiveTransitions() {
    return this.activeTransitions.length > 0
  }
}
